package com.wang.common.dao.impl;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import com.wang.common.util.ConstatFinalUtil;
import com.wang.common.util.DbUtil;
import com.wang.common.util.PageInfoUtil;
import lombok.extern.log4j.Log4j2;

/**
 * 此类是所有Dao的公共类
 * <p>
 * T:表示泛型,在父类我不知道dao对应的是哪张表,想在父类里面使用某个类型(不定的)的对象,那就加T,(占位符)
 * 让子类来确定哪张表的Dao
 * T:暂时理解为Object,在使用的时候,根据指定的类型来确定T的类型;
 * 在父类里面不能出现子类相关的类型
 *
 * @author wangshMac
 */
@Log4j2
public class BaseDaoImpl<T> {
    /**
     * 加载驱动和获取链接
     */
    protected DbUtil dbUtil = DbUtil.getInstance();

    /**
     * 把数据库里面的记录,变成java的对象
     * 利用反射
     * <p>
     * ~将返回值由Admins换成了Object
     * ~增加了一个参数:Object,参数的Object和返回值一样;
     * <p>
     * 在调用处的时候,传上Dao对应的类型
     *
     * @param rs 结果集
     * @return 记录对应的对象
     * @throws SQLException sql异常
     */
    protected T convertFromDb(T t, ResultSet rs) throws SQLException {
        /*
         * ~获取Class对象
         * ~获取所有的method
         * */
        Class cla = t.getClass();
        /* new一个对象
         * 所有的属性全部是空的,
         * 调用set方法赋值
         *  */
        Object returnObj = null;
        try {
            returnObj = cla.newInstance();
        } catch (InstantiationException e1) {
            e1.printStackTrace();
        } catch (IllegalAccessException e1) {
            e1.printStackTrace();
        }

        /* 获取所有的字段 */
        Field[] fields = cla.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            try {
                Field field = fields[i];
                /* 字段名 */
                String name = field.getName();
                /*
                 * id = Id;
                 * setId ;
                 * */
                String setName = "set" + name.substring(0, 1).toUpperCase() + name.substring(1, name.length());
                /*
                 * field.getType():获取属性的类型,
                 * 返回值是Class
                 * */
                //System.out.println(i + "===" + name + "--->" + setName + "--->" + field.getType().getSimpleName());
                Class fieldTypeClass = field.getType();
                /* set方法
                 * setName
                 *
                 * getMethod
                 * 参数1:指的是方法名;set+属性(首字母大写)
                 * 参数2:类型;有int,byte,string,Date
                 * 有待扩展,有可能填写Double,四类八种,都考虑到
                 *  */
                Method setMethod = null;
                Object dbObj = null;
                if (fieldTypeClass == int.class || fieldTypeClass == Integer.class) {
                    setMethod = cla.getMethod(setName, int.class);
                    dbObj = rs.getInt(name);
                } else if (fieldTypeClass == byte.class || fieldTypeClass == Byte.class) {
                    setMethod = cla.getMethod(setName, byte.class);
                    dbObj = rs.getByte(name);
                } else if (fieldTypeClass == double.class || fieldTypeClass == Double.class) {
                    setMethod = cla.getMethod(setName, double.class);
                    dbObj = rs.getDouble(name);
                } else if (fieldTypeClass == String.class) {
                    setMethod = cla.getMethod(setName, String.class);
                    dbObj = rs.getString(name);
                } else if (fieldTypeClass == Date.class) {
                    setMethod = cla.getMethod(setName, Date.class);
                    dbObj = rs.getTimestamp(name);
                }

                if (setMethod != null) {
                    /* 执行setMethod
                     * setName("测试");的参数是赋值,要从数据库中取
                     *
                     * 参数1:是对象,Class对应的对象
                     * 参数2:方法的形参对应的实参;
                     * setName方法可是木有返回值
                     *  */
                    setMethod.invoke(returnObj, dbObj);
                }
            } catch (Exception e) {
                //e.printStackTrace();
            }
        }
        return (T) returnObj;
    }

    /**
     * 父类中查询一条记录
     * @param sql   sql语句
     * @param paramsList    参数
     * @param t 对象
     * @return  对象
     */
    protected T findOneBase(String sql, List<Object> paramsList, T t) {
        /*-----------------标准的JDBC-------------------*/
        PreparedStatement ps = null;
        ResultSet rs = null;
        Connection connection = this.dbUtil.getConnection();
        try {
            ps = connection.prepareStatement(sql);
            /* 设置条件
             * 从Map中取值的时候,使用的时候好大方,好气场,一定确定一下有木有放进去(四大作用域)
             *
             * 参数1:第几个?
             * 参数2:为第几个?赋值
             *  */
            /* 有多少个?,就要设置多个条件 */
            int count = 1;
            for (Iterator iterator = paramsList.iterator(); iterator.hasNext(); ) {
                Object paramObj = (Object) iterator.next();
                ps.setObject(count, paramObj);
                /* 计数器 */
                count++;
            }

            rs = ps.executeQuery();
            /* 如果是多条,使用循环
             * 如果是单条,使用判断 */
            if (rs.next()) {
                /* 将数据库里面的记录变成JAVA对象 */
                t = convertFromDb(t, rs);
                return t;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            this.dbUtil.closeAll(connection, ps, rs);
        }
        return null;
    }

    /**
     * 查询列表,不分页
     *
     * @param sql   sql语句
     * @param paramsList    参数
     * @param t 对象
     * @return  对象
     */
    protected List<T> find(String sql, List<Object> paramsList, T t) {
        /*-----------------标准的JDBC-------------------*/
        List<T> resultList = new ArrayList<T>();

        PreparedStatement ps = null;
        ResultSet rs = null;
        Connection connection = this.dbUtil.getConnection();
        try {
            /* 查询所有数据 */
            ps = connection.prepareStatement(sql);
            /* 设置条件 */
            int count = 1;
            for (Iterator iterator = paramsList.iterator(); iterator.hasNext(); ) {
                Object paramObj = (Object) iterator.next();
                ps.setObject(count, paramObj);
                count++;
            }
            rs = ps.executeQuery();

            /* 如果是多条,使用循环
             * 如果是单条,使用判断 */
            while (rs.next()) {
                /* 将数据库里面的记录变成JAVA对象 */
                t = this.convertFromDb(t, rs);
                /* 把admins放到集合中 */
                resultList.add(t);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            this.dbUtil.closeAll(connection, ps, rs);
        }
        return resultList;
    }

    /**
     * 查询列表,分页
     * @param pageInfoUtil  分页对象
     * @param sql   sql语句
     * @param paramsList    参数
     * @param t 对象
     * @return  对象
     */
    protected List<T> findByPage(PageInfoUtil pageInfoUtil, String sql, List<Object> paramsList, T t) {
        /*-----------------标准的JDBC-------------------*/
        List<T> resultList = new ArrayList<T>();
        PreparedStatement ps = null;
        ResultSet rs = null;
        Connection connection = this.dbUtil.getConnection();
        try {
            if (pageInfoUtil != null) {
                String fromIndex = "from";
                String orderbyIndex = "order by";
                String countSQL = "select count(*) " + sql.substring(sql.indexOf(fromIndex), sql.indexOf(orderbyIndex));
                log.info("==总条数sql:{}==", countSQL);
                log.info("==总条数条件:{}==", paramsList);
                /* 将sql语句发送到数据库 */
                ps = connection.prepareStatement(countSQL);
                /* 设置条件 */
                int count = 1;
                for (Iterator iterator = paramsList.iterator(); iterator.hasNext(); ) {
                    Object paramObj = (Object) iterator.next();
                    ps.setObject(count, paramObj);
                    count++;
                }
                rs = ps.executeQuery();
                /* 总记录数,肯定有一条结果 */
                if (rs.next()) {
                    /* 取第一列 */
                    int totalRecord = rs.getInt(1);
                    pageInfoUtil.setTotalRecord(totalRecord);
                }
                rs.close();
                ps.close();

                /* select * from a_admins limit 1,5*/
                sql = sql + " limit ? , ? ";
                /* 查询分页数据
                把条件统一放到List中 */
                paramsList.add(pageInfoUtil.getCurrRecord());
                paramsList.add(pageInfoUtil.getPageSize());

                log.info("==取数据sql:{}==", sql);
                log.info("==取数据条件:{}==", paramsList);
            }

            /* 查询所有数据 */
            ps = connection.prepareStatement(sql);
            /* 设置条件 */
            int count = 1;
            for (Iterator iterator = paramsList.iterator(); iterator.hasNext(); ) {
                Object paramObj = (Object) iterator.next();
                ps.setObject(count, paramObj);
                count++;
            }
            rs = ps.executeQuery();

            /* 如果是多条,使用循环
             * 如果是单条,使用判断 */
            while (rs.next()) {
                /* 将数据库里面的记录变成JAVA对象 */
                t = this.convertFromDb(t, rs);
                /* 把admins放到集合中 */
                resultList.add(t);
            }
        } catch (SQLException e) {
            log.error("操作数据库报错;sql:{},参数:{}", sql, paramsList, e);
        } finally {
            this.dbUtil.closeAll(connection, ps, rs);
        }
        return resultList;
    }

    /**
     * 执行一条sql语句
     */
    protected int executeSqlUpdate(String sql, List<Object> paramsList) {
        /*-------JDBC------*/
        int res = 0;
        Connection connection = this.dbUtil.getConnection();
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            /* 设置条件 */
            int parameterIndex = 1;
            for (Iterator iterator = paramsList.iterator(); iterator.hasNext(); ) {
                Object paramObj = (Object) iterator.next();
                ps.setObject(parameterIndex, paramObj);
                parameterIndex++;
            }
            /*
             * 返回值:res的含义是:执行sql语句以后对数据库影响的条数
             * */
            res = ps.executeUpdate();

            /* 获取主键 */
            rs = ps.getGeneratedKeys();
            if (rs.next()) {
                int id = rs.getInt(1);
                /* 保存方法执行的结果时,要么使用返回值,要么参数返回 */
                paramsList.add(id);
            }
        } catch (SQLException e) {
            log.error("操作数据库报错;sql:{},参数:{}", sql, paramsList, e);
        } finally {
            this.dbUtil.closeAll(connection, ps, rs);
        }
        return res;
    }
}
